using System;
using System.Windows.Forms;
using System.Xml;
using System.Xml.XPath ;
using EnvDTE;
using AopDotNet.CodeModelXMLTreeBuilder;
using AopDotNet.Editor ;
using System.Collections ;
using System.Text.RegularExpressions  ;
using System.Text;
using System.IO ;

using System.CodeDom.Compiler  ;
using System.CodeDom ;

namespace AopDotNet
{
	namespace AspectWeaver
	{
		/// <summary>
		/// 
		/// </summary>
		public class AspectCodeTranslator
		{
			private CodeFunction aspectFunction;
			private XmlNode advisedMethod;
			private string adviceType;
			private WeavingLevel weavingLevel;
			private ICodeGenerator codeGenerator;
			private Weaver theWeaver;
			private EditPoint startEP;
			private EditPoint endEP;

			public AspectCodeTranslator(CodeFunction aspectFunction,XmlNode advisedMethod,string adviceType,Weaver theWeaver,ICodeGenerator codeGenerator )
			{
				this.adviceType =adviceType;
				this.advisedMethod =advisedMethod;
				this.aspectFunction =aspectFunction;
				this.weavingLevel=weavingLevel;
				this.codeGenerator =codeGenerator;
				this.theWeaver = theWeaver;
			}

			/// <summary>
			/// sets the start point and the length of the advised code snippet ex: var.SetX()
			/// </summary>
			/// <param name="startIndex"></param>
			/// <param name="endIndex"></param>
			public int SetAdvisedCodeBoundaries(EditPoint start,EditPoint end)
			{
			
				this.startEP=start ;
				this.endEP =end ;
				return 0;
			}

			/*This function should be resused for all types of pointcut not just execution */
			public StringBuilder TranslateAspectSpecificCode()
			{
				StringBuilder strAspectFunction=new StringBuilder (Helper.GetFunctionBody(aspectFunction ));
			
				TranslateCurrentJoinPointGetCurrentMethod(ref strAspectFunction);
				TranslateProceed(ref strAspectFunction);
				TranslateCurrentJoinPointGetCurrentObject(ref strAspectFunction);
			
				return strAspectFunction;	
			}
			void TranslateProceed(ref StringBuilder strAspectFunction)
			{
				//find all calls to Proceed()
			
				CodeMethodInvokeExpression proceedInvoke=new CodeMethodInvokeExpression(
					new CodeTypeReferenceExpression(@"currentJoinPoint\s*"), 
					@"\s*Proceed\s*",new CodeExpression[]{new CodeSnippetExpression (@"$$$\s*")} );
			
				System.IO.StringWriter  strProceedCall=new System .IO .StringWriter ();
				codeGenerator.GenerateCodeFromExpression (proceedInvoke,strProceedCall,null);
			
				string original=strProceedCall.ToString ();
				int index=original.IndexOf ("$$$");
				original=original.Remove (index,3);
				original=original.Replace ("(","[(]");
				original=original.Replace (")","[)]");
			
			
				MatchCollection matchedCalls=Regex.Matches  (strAspectFunction.ToString (),original);
				if (matchedCalls.Count ==0)//no calls to Proceed() was found
					return;
				if ((matchedCalls.Count >0)&&(adviceType!="around"))/*Missing*/ //proceed may work with bef we aft??
				{
					MessageBox.Show ("Proceed() Call is not valid here","AopDotNetAddIn");
					return;
				}
			
				string methodID=this.advisedMethod.Attributes ["id"].Value ;
				CodeFunction cfAdvisedMethod=(CodeFunction)theWeaver.AdvisedCodeModelTree.codeElements[Int32.Parse(methodID)];
				//if Weaving Level =Method
				//generate new function with different name
				if (weavingLevel==WeavingLevel.Method )//method level then wrap the advised method in a new one 
				{
					/*REMF*/
					//EditPoint e=cfAdvisedMethod.GetStartPoint (vsCMPart.vsCMPartWhole ).CreateEditPoint ();
					EditPoint e=cfAdvisedMethod.GetStartPoint (vsCMPart.vsCMPartWhole ).CreateEditPoint ();
					theWeaver.editingMonitor.UpdateEditPoint (ref e);
				
					//copy the old function
					//string func=e.GetText (cfAdvisedMethod.GetEndPoint (vsCMPart.vsCMPartWhole ));
					EditPoint endp=cfAdvisedMethod.GetEndPoint (vsCMPart.vsCMPartWhole ).CreateEditPoint ();
					theWeaver.editingMonitor .UpdateEditPoint (ref endp);
					string func=e.GetText (endp);
					//MessageBox.Show (func);
			
					index=func.IndexOf (advisedMethod.Name );
					if (index!=-1)
						func=func.Insert (index,"Wrapped"); //change its name to WrappedXXX
					/*REMF*/
					//add the original method after the new WrappedXXX Method
					EditPoint ep=cfAdvisedMethod.EndPoint .CreateEditPoint ();
					theWeaver.editingMonitor .StartLog (ref ep,false);
					ep.Insert ("\n");
					ep.Insert (func);
					ep.Insert ("\n");/*Inser*/
					theWeaver.editingMonitor .StartLog (ref ep,false);
			
					//replace each call for proceed with the wrapped method
					//el return/*Missing*/ what return statement should do
					CodeExpression [] parameters;
			
					XmlNode parametersNode=advisedMethod.SelectSingleNode ("parameters");
					if (parametersNode!=null)
					{
						XmlNodeList parametersNodes=parametersNode .SelectNodes ("parameter");
						parameters =new CodeExpression [parametersNodes .Count ];
						for (int i=0;i<parametersNodes .Count ;i++)
						{
							parameters[i]=new CodeVariableReferenceExpression (parametersNodes[i].Attributes ["name"].Value );
						}
					}
					else
						parameters=new CodeExpression []{new CodeSnippetExpression (@"$$$")};
			
					CodeMethodInvokeExpression wrappedMethodCall=new CodeMethodInvokeExpression (new CodeThisReferenceExpression (),"Wrapped"+advisedMethod.Name ,parameters);
					System.IO.StringWriter  strWrappedMethodCall=new System .IO .StringWriter ();
					codeGenerator.GenerateCodeFromExpression (wrappedMethodCall,strWrappedMethodCall,null);
			
					string translated=strWrappedMethodCall.ToString ();
					if (parametersNode==null)
					{
						index=translated.IndexOf ("$$$");
						translated=translated.Remove (index,3);
					}
			
					for (int i=0;i<matchedCalls.Count ;i++)
					{
						strAspectFunction.Replace (matchedCalls[i].Value ,translated,matchedCalls[i].Index ,matchedCalls[i].Length );//remove 3and el index beta3 el matched
					}
				}
				else //statement level 
				{
					//proceed should do what the advised code was supposed to do
					//ex if the code that was advised is call(Point.Set*)
					//then proceed should call (Point.Set*)
					/*REMN*/
					//string translated=Helper.GetStatementInFunction (cfAdvisedMethod,this.startEP ,this.endEP);
					string translated=this.startEP.GetText (this.endEP );
					for (int i=0;i<matchedCalls.Count ;i++)
					{
						//remove 3and el index beta3 el matched
						strAspectFunction.Replace (matchedCalls[i].Value ,translated,matchedCalls[i].Index ,matchedCalls[i].Length );
					}
																
				}
			}
			void TranslateCurrentJoinPointGetCurrentObject(ref StringBuilder strAspectFunction)
			{
				System.IO .StringWriter strThisRef=new StringWriter ();
				if (advisedMethod.Attributes ["static"].Value =="true")
				{	
					CodePrimitiveExpression nullExpr=new CodePrimitiveExpression (null);
					codeGenerator .GenerateCodeFromExpression (nullExpr,strThisRef,null);
				}
				else
				{
					CodeThisReferenceExpression thisRef=new CodeThisReferenceExpression ();
					codeGenerator .GenerateCodeFromExpression (thisRef,strThisRef,null);
				}
			

				CodeMethodInvokeExpression invoke=new CodeMethodInvokeExpression(
					new CodeTypeReferenceExpression(@"currentJoinPoint\s*"), 
					@"\s*GetCurrentObject\s*",new CodeExpression[]{new CodeSnippetExpression (@"$$$\s*")} );
			
				System.IO.StringWriter  strCurrentJpGetMethodCall=new System .IO .StringWriter ();
				codeGenerator.GenerateCodeFromExpression (invoke,strCurrentJpGetMethodCall,null);
			
				string original=strCurrentJpGetMethodCall.ToString ();
				int index=original.IndexOf ("$$$");
				original=original.Remove (index,3);
				original=original.Replace ("(","[(]");
				original=original.Replace (")","[)]");
		
			
			
				MatchCollection matchedCalls=Regex.Matches  (strAspectFunction.ToString (),original);
				for (int i=0;i<matchedCalls.Count ;i++)
				{
					strAspectFunction.Replace (matchedCalls[i].Value ,strThisRef.ToString (),matchedCalls[i].Index ,matchedCalls[i].Length );//remove 3and el index beta3 el matched
				}
			}
			void TranslateCurrentJoinPointGetCurrentMethod(ref StringBuilder strAspectFunction)
			{
				string methodName,methodAccess,methodReturn,methodStatic;
				methodName =advisedMethod.Attributes ["name"].Value ;
				methodAccess  =advisedMethod.Attributes ["access"].Value ;
				methodReturn= advisedMethod.Attributes ["return"].Value ;
				methodStatic=advisedMethod.Attributes ["static"].Value  ;
				
				CodeFieldReferenceExpression BindFlag1=null,BindFlag2=null;
				switch (methodAccess)
				{
					case "vsCMAccessPublic":
						BindFlag1=new CodeFieldReferenceExpression(new CodeTypeReferenceExpression ("BindingFlags"),"Public");
						break;
					case "vsCMAccessPrivate":
						BindFlag1=new CodeFieldReferenceExpression(new CodeTypeReferenceExpression ("BindingFlags"),"NonPublic");
						break;
					case "vsCMAccessProtected":
						BindFlag1=new CodeFieldReferenceExpression(new CodeTypeReferenceExpression ("BindingFlags"),"NonPublic");
						break;
					case "vsCMAccessProject":
						BindFlag1=new CodeFieldReferenceExpression(new CodeTypeReferenceExpression ("BindingFlags"),"NonPublic");
						break;
				}
			
				if (methodStatic =="true")
					BindFlag2=new CodeFieldReferenceExpression(new CodeTypeReferenceExpression ("BindingFlags"),"Static");
				else
					BindFlag2=new CodeFieldReferenceExpression(new CodeTypeReferenceExpression ("BindingFlags"),"Instance");

				CodeExpression [] parameters=new CodeExpression [5];

				XmlNode paramsNode;
						
				paramsNode=advisedMethod.SelectSingleNode ("parameters");
				if (paramsNode==null)
					parameters[3]=new CodeArrayCreateExpression("Type");
				else
				{
					CodeExpression []paramsOfParams=new CodeExpression [paramsNode.ChildNodes .Count ];
					for (int i=0;i<paramsNode.ChildNodes.Count ;i++ )
						paramsOfParams[i]=new CodeTypeOfExpression (paramsNode.ChildNodes [i].Attributes ["type"].Value);
				
					parameters[3]=new CodeArrayCreateExpression(typeof(Type),paramsOfParams);
				}
				parameters[2]=parameters[4]=new CodePrimitiveExpression (null);
				parameters[0]=new CodePrimitiveExpression (methodName);
				parameters[1]=new CodeBinaryOperatorExpression (BindFlag1,CodeBinaryOperatorType .BitwiseOr ,BindFlag2);
			
				System.IO.StringWriter  tempWriter=new System .IO .StringWriter ();
				codeGenerator .GenerateCodeFromExpression (parameters[3],tempWriter,null);
				string temp=tempWriter .ToString ();
				temp=temp.Replace ('@',' ');//i'm sure that no types can contain @?????????
				parameters[3]=new CodeSnippetExpression (temp);
			
				//GetType().GetMethod(...)			
				CodeMethodInvokeExpression getMethodCall=new CodeMethodInvokeExpression(
					new CodeMethodInvokeExpression(new CodeThisReferenceExpression(),"GetType",new CodeExpression[]{new CodeSnippetExpression (@"$$$")}),
					"GetMethod",parameters);
				System.IO.StringWriter  strGetTypeGetMethodCall=new System .IO .StringWriter ();
				codeGenerator.GenerateCodeFromExpression (getMethodCall,strGetTypeGetMethodCall,null);
			
				string translated=strGetTypeGetMethodCall.ToString ();
				int index=translated.IndexOf ("$$$");
				translated=translated.Remove (index,3);
				///<perform>
				///create call to currentJoinPoint.GetCurrentMethod
				///</perform>
			
				CodeMethodInvokeExpression invoke=new CodeMethodInvokeExpression(
					new CodeTypeReferenceExpression(@"currentJoinPoint\s*"), 
					@"\s*GetCurrentMethod\s*",new CodeExpression[]{new CodeSnippetExpression (@"$$$\s*")} );
			
				System.IO.StringWriter  strCurrentJpGetMethodCall=new System .IO .StringWriter ();
				codeGenerator.GenerateCodeFromExpression (invoke,strCurrentJpGetMethodCall,null);
			
				string original=strCurrentJpGetMethodCall.ToString ();
				index=original.IndexOf ("$$$");
				original=original.Remove (index,3);
				original=original.Replace ("(","[(]");
				original=original.Replace (")","[)]");
			
			
				MatchCollection matchedCalls=Regex.Matches  (strAspectFunction.ToString (),original);
				for (int i=0;i<matchedCalls.Count ;i++)
				{
					strAspectFunction.Replace (matchedCalls[i].Value ,translated,matchedCalls[i].Index ,matchedCalls[i].Length );//remove 3and el index beta3 el matched
				}
			}

		}
	}
}